# coding:utf-8
# numpy提供序列并行化方法，更有统计学上很多方法的实现。如期望，方差，标准差等。
# numpy是通过广播机制作用于每一个元素，是一种并行化思想，而普通list是作用于整体。
import numpy as np
# 柯里化一个print
printn = lambda *x: print(*x, sep='\n')


# 生成ndarray的方法汇总,zeros(),ones(),empty()要传入长度或者形状(shape)
def makenp():
    a1 = np.zeros(5)
    a2 = np.ones(5)
    # 三行两列，全是0
    a3 = np.zeros((3, 2))
    # 两行三列三层，值随机；即3个维度，每个维度变量数2，3，3
    a4 = np.empty((2, 3, 3))
    np_list = np.arange(6)  # range的数组版
    # print(np_list)
    # XXX_like就是使用参数里的数组的shape
    a5 = np.ones_like(np_list)
    a6 = np.zeros_like(np_list)
    # eye()得到对角线全为1的单位矩阵
    a7 = np.eye(3)
    # 普通list作为参数，初始化ndarray
    data = [[1, 2, 3, 4], [5, 6, 7, 8]]
    a8 = np.array(data)
    print("n8的维度:", a8.ndim)
    # 等间隔生成指定数量元素序列。在0~1之间等间隔生成10个元素的序列
    a9 = np.linspace(0, 1, 10)
    a10 = np.arange(15)
    # 使用eval()把字符串变成变量名
    for i in range(1, 11):
        print("a" + str(i) + "=", eval("a" + str(i)))

    # ndarray数据类型
    arr = np.array([1, 2, 3, 4, 5])
    print("arr的数据类型：", arr.dtype)
    float_arr = arr.astype(np.float64)  # 数组.astype转换数据类型,总会创建一个新的数组
    print("float_arr的数据类型：", float_arr.dtype)
    # 字符串数组的数字，也可转为数值形式
    numeric_strings = np.array(['1.25', '-9.6', '42'], dtype=np.string_)
    print("转换为数值:", numeric_strings.astype(float))
    # 使用简写的dtype，如u4表示4个字节的整形，int32
    empty_unit32 = np.empty(8, dtype='u4')
    print("简写的dtype:", empty_unit32)


makenp()


# ---------------------------------------------------------------
# numpy计算，对应元素相操作
def calnumpy():
    arr = np.array([[1., 2., 3.], [4., 5., 6.]])
    print("原数组:", arr, sep='\n')
    print("a*a乘法:", arr * arr, sep='\n')
    print("a-a减法：", arr - arr, sep='\n')
    print('1/a结果:', 1 / arr, sep='\n')
    print("乘方:", arr**0.5, sep='\n')
    arr2 = np.array([[0., 4., 1.], [7., 2., 12.]])
    print("数组比较:", arr2 > arr, sep='\n')


# calnumpy()
# ----------------------------------------------------------------
# 修改ndarray
def modnp():
    a9 = np.linspace(0, 1, 10)
    # 1、插入数据 np.insert(arr,obj,xxx)
    a10 = np.insert(a9, 0, 10)  # 首位插入数字10,返回新的ndarray
    print(a10)
    a11 = np.sort(a10)
    printn("顶级方法np.sort,不改变原始数据:", a11, "原始数据:", a10)
    a10.sort()  # 从小到大排序，改变原数组
    printn("数组方法arr.sort:改变原数组", a10)
    # 多维数组在任一轴上排序
    arr = np.random.randn(5, 3)
    printn("原数组:", arr)
    # "横向排序axis=1
    arr.sort(1)
    printn("查看原数组:", arr)
# modnp()


# ------------------------------------------------------------------
# 切片操作，0:2,:5，冒号某侧省略，表示从头开始或直到末尾
# 数组切⽚是原始数组的视图。这意味着数据不会被复制，
# 视图上的任何修改都会直接反映到源数组上
def indextest():
    arr = np.arange(10)
    print("索引1:", arr[5], sep='\n')
    print('索引2:', arr[5:8], sep='\n')
    # 索引赋值
    arr[5:8] = 12  # ⼀个标量值赋值给⼀个切⽚时，该值会⾃动传播到整个选区
    print('索引赋值后的数组:', arr, sep='\n')
    # 如果需要切片的副本，需明确使用复制操作
    print("切片副本:", arr[5:8].copy(), sep='\n')

    # 对于二维数组，各索引位置上的元素不再是标量，而是一维数组
    # 二维数组的索引方式，轴0作为行，轴1作为列
    arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
    print("二维数组切片1:", arr2d[2], sep='\n')
    # 可以对各个元素进行递归访问，但这样需要做的事情有点多。可以传入一个以逗号隔开的索引列表来选取单个元素
    a = arr2d[0][2]  # 递归访问
    b = arr2d[0, 2]  # 列表选取访问
    print("二维数组切片2的两种方法:", a, b, sep='\n')

    # 多维数组中，如果省略了后⾯的索引，则返回对象会是⼀个维度低⼀点的ndarray
    arr3d = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
    print("多维数组索引:", arr3d[0], sep='\n')
# indextest()


# 切片
def slicetest():
    arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
    print("原二维数组:", arr2d, sep='\n')
    # 默认沿着0轴（即第⼀个轴）切⽚的。表达式arr2d[:2]可以被认为是“选取arr2d的前两⾏”
    print("前2行切片arr2d[:2]:", arr2d[:2], sep='\n')
    # 可以⼀次传⼊多个切⽚，就像传⼊多个索引那样,[:2,1:]表示前2行，第1列(注意有第0列)开始
    print("多个切片arr2d[:2,1:]:", arr2d[:2, 1:], sep='\n')
    # 通过将整数索引和切⽚混合，可以得到低维度的切⽚
    print("第⼆⾏的前两列arr2d[1,:2]:", arr2d[1, :2], sep='\n')
    print("第三列的前两行arr2d[:2,2]:", arr2d[:2, 2], sep='\n')
    # “只有冒号”表示选取整个轴，可以像下⾯这样只对⾼维轴进⾏切⽚
    print("对1轴进行切片，0轴全要:", arr2d[:, :1], sep='\n')

    # 对切⽚表达式的赋值操作也会被扩散到整个选区
    arr2d[:2, 1:] = 0
    print("切片赋值后的arr:", arr2d, sep='\n')
# slicetest()


# 布尔索引--选取数组中的数据，将总是创建数据的副本，即使返回⼀模⼀样的数组也是如此
def boolindex():
    names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
    data = np.random.randn(7, 4)
    print("names:", names, 'data:', data, sep='\n')
    # 假设每个名字都对应data数组中的⼀⾏，⽽我们想要选出对应于名字"Bob"的所有⾏
    # bool型数组的长度必须与索引轴的长度一致
    print('bool索引结果:', data[names == 'Bob'], sep='\n')
    # bool型数组与切片混用
    print("bool索引，切片混用:", data[names == 'Bob', 2:], sep='\n')
    # 选择除"Bob"以外的其他值，既可以使⽤不等于符号（!=），也可以通过~对条件进⾏反转
    print(
        "选取以外的值的两种方法:",
        data[names != 'Bob'],
        data[~(names == 'Bob')],
        sep='\n')
    # 组合多个bool条件
    mask = (names == 'Bob') | (names == 'Will')
    print("多bool条件:", data[mask], sep='\n')

    # 通过bool型数组设置值，是一种常用的手段
    data[data < 0] = 0
    print('bool赋值:', data, sep='\n')
    # 通过⼀维布尔数组设置整⾏或列的值
    data[names != 'Joe'] = 7
    print("bool整行列赋值:", data, sep='\n')
# boolindex()


# ---------------------------------------------------------------------
# 花式索引--利用数组进行索引，创建副本数组，与切片不一样
def flowerindex():
    arr = np.empty((8, 4))
    for i in range(8):
        arr[i] = i
    print('原数组:', arr, sep='\n')
    # 以特定顺序选取子集，只需传⼊⼀个⽤于指定顺序的整数列表或ndarray即可
    print("指定顺序索引:", arr[[4, 3, 0, 6]], sep='\n')
    print("指定顺序索引(负数):", arr[[-3, -5, -7]], sep='\n')

    # ⼀次传⼊多个索引数组会有⼀点特别。它返回的是⼀个⼀维数组。
    arr = np.arange(32).reshape((8, 4))
    print('原数组2:', arr, sep='\n')
    print("多列表索引:", arr[[1, 5, 7, 2], [0, 3, 1, 2]],
          sep='\n')  # 选取(1,0),(5,3),(7,1),(2,2)上的数据
    # 选取矩阵的行列子集#先list选行，再用行列混合切片选列
    print("行列子集:", arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]], sep='\n')
# flowerindex()


# 数组转置和轴对换
# 转置是重塑的⼀种特殊形式，它返回的是源数据的视图（不会进⾏任何复制操作）
def transtest():
    arr = np.arange(15).reshape(3, 5)
    # arr.T # 不改变原数组，返回原数组的视图
    print("原数组", arr, "转置数组:", arr.T, sep='\n')
    # np.dot计算矩阵内积，会用到转置
    arr = np.random.randn(6, 3)
    print("矩阵内积:", np.dot(arr.T, arr), sep='\n')
    # transpose需要得到一个由轴编号组成的元组，才能对这些轴进行转置
    arr = np.arange(16).reshape(2, 2, 4)
    '''
    `i` in the `j`-th place in the tuple means `arr`'s `i`-th axis becomes `arr.transpose()`'s `j`-th axis.
    (1,0,2),是针对正常顺序元组(0,1,2)，所以是0轴和1轴互换。
    数字2在索引2上，即第3坐标不变，当作一个整体作为索引1(即第2坐标的元素)，这样就把整个数组降维成2维。
    0轴和1轴互换，就成了矩阵转置。此处就是2*2矩阵，转置就是副对角线互换。
    '''
    print("原数组2:\n", arr, "\n转置数组2:\n", arr.transpose(1, 0, 2))
    # swapaxes方法:
    # 如下，0轴不变，1、2轴互换，即保证0轴的元素结构不动，依然是2个元素，每个元素里进行转置。
    print("原数组3:\n", arr, "\n轴对换数组3:\n", arr.swapaxes(1, 2))
# transtest()


# ----------------------------------------------------------------------------------------
# 通用函数：对ndarray的数据执行元素级运算的函数。“简单函数的矢量化包装器”。
'''
一元函数：abs,fabs,sqrt,square,exp,log,log10,log2,log1p,sign,ceil,floor,rint,modf,isnan,isfinite,isinf
        cos,cosh,sin.sinh,tan,tanh,arccos,arccosh,arcsin,arcsinh,arctan,arctanh,logical_not
二元函数：add,subtract,multiply,divide,floor_divide,power,maximum,fmax,minimum,fmin,mod,copysign,greeter,
greater_equal,less,less_equal,logical_and,logical_or,logical_xor
# 数组的集合运算：unique,intersect1d--交集,union1d--并集,in1d,setdiff1d--差集,setxor1d--对称差
'''


def ufunctest():
    arr = np.arange(10)
    print("原数组:", arr, sep='\n')
    # 一元ufunc，如sqrt各元素平方根根，exp个元素的指数e的x次方
    print("平方根:", np.sqrt(arr), "e的x次方:", np.exp(arr), sep='\n')
    # 二元ufunc，接受2个数组，返回一个结果数组
    x = np.random.randn(8)
    y = np.random.randn(8)
    print("x,y及x、y的里的对应最大值分别为:", x, y, np.maximum(x, y), sep='\n')
    # 特殊情况，会有ufunc返回多个数组，如modf，是divmod的矢量化版本，返回浮点数数组的小数和整数部分
    arr = np.random.randn(7)
    remainder, whole_part = np.modf(arr)
    print("数组：", arr, "小数部分:", remainder, "整数部分:", whole_part, sep='\n')
    # ufunc可以接受一个out可选参数，若设置为元素本身，可在原数据上操作
    np.sqrt(arr, arr)  # 第二个就是out参数
    print('使用out后:', arr, sep='\n')
# ufunctest()


# ---------------------------------------------------------------------
# 利用数组进行数据处理
'''
用数组表达式替代循环的做法，通常被称为矢量化。
np.meshgrid函数接受2个一维数组，并产生2个二维矩阵（对应两个数组中所有的(x,y)对）
假设在一组值上计算函数。
'''


def arraydealtest():
    points = np.arange(-5, 5, 0.01)  # 10000个点
    points2 = np.arange(6, 10, 0.01)
    # xs是一个数组，里面每个元素均是一套坐标(-5到5,间隔0.01),ys同理
    # 即10000个x方向线段，10000个y方向线段相交
    xs, ys = np.meshgrid(points, points2)
    print('meshgrid的第二个矩阵:', ys, sep='\n')
    z = np.sqrt(xs**2 + ys**2)
    print("平方和的平方根:", z, sep='\n')
    # 将此结果可视化
    import matplotlib.pyplot as plt
    # plt.cm.gray即'gray'字符串
    plt.imshow(z, cmap=plt.cm.gray)
    plt.colorbar()
    # plt.imshow(z,cmap='gray');plt.colorbar()
    # 数学表达式mathtext要包含在一对"$"中，数学函数要用'\'转义
    plt.title("Image plot of $\sqrt{x^2+y^2}$ for a grid of values")
    plt.show()


arraydealtest()


# 将条件逻辑表述为数组运算
def testif():
    xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])
    yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])
    cond = np.array([True, False, True, True, False])
    # 假设我们根据cond中的值选取xarr和yarr：当cond为True选xarr，负yarr
    # 1、使用不理想的列表推导式,速度不够快，不能用于多维数组
    result = [(x if c else y) for x, y, c in zip(xarr, yarr, cond)]
    print("列表推导式的结果:", result, sep='\n')
    # 2、使用np.where,if三元表达式的矢量形式
    result = np.where(cond, xarr, yarr)
    print('np.where的结果:', result, sep='\n')
    # np.where的第⼆个和第三个参数不必是数组，它们都可以是标量值。在数据分析⼯作中，where通常⽤于根据另⼀个数组⽽产⽣⼀个新的数组
    # 假设有⼀个由随机数据组成的矩阵，你希望将所有正值替换为2，将所有负值替换为－2。
    arr = np.random.randn(4, 4)
    result = np.where(arr > 0, 2, -2)
    print("原数组:", arr, 'np.where选取的数组:', result, sep='\n')
    # np.where可以将标量和数组结合起来，如使用2替换正值，其他不变
    # print("仅替换正值:",np.where(arr>0,2,arr),sep='\n')
    printn("仅替换正值:", np.where(arr > 0, 2, arr))
# testif()


# 数学和统计方法
'''
sum--和，mean--均值，std、var--标准差、方差，min、max-最小、最大值，argmin、argmax-第一个最小、最大值的索引
cumsum-累加，cumprod--累积
'''


def teststatis():
    # 可以通过数组上的⼀组数学函数对整个数组或某个轴向的数据进⾏统计计算
    # sum、mean以及标准差std等聚合计算（aggregation，通常叫做约简（reduction））既可以当做数组的实例⽅法调⽤，也可以当做顶级NumPy函数使⽤。
    arr = np.random.randn(5, 4)
    printn(
        "原数组:", arr, "均值:", arr.mean(), "np计算均值:", np.mean(arr), "和:",
        arr.sum())
    # mean和sum这类的函数可以接受⼀个axis选项参数，⽤于计算该轴向上的统计值，最终结果是⼀个少⼀维的数组
    # arr.mean(1)是“计算⾏的平均值”，arr.sum(0)是“计算每列的和”。,axis=1表示1轴结构的元素进行操作。
    printn("1轴上的均值:", arr.mean(axis=1), "0轴上的和:", arr.sum(axis=0))
    # 其他如cumsum和cumprod之类的⽅法则不聚合，⽽是产⽣⼀个由中间结果组成的数组
    arr = np.array([0, 1, 2, 3, 4, 5, 6, 7])
    printn('cumsum的值:', arr.cumsum())
    # 多维数组中，累加函数（如cumsum）返回的是同样⼤⼩的数组，但是会根据每个低维的切⽚沿着标记轴计算部分聚类
    arr = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
    printn(
        "数组:",
        arr,
        "0轴的累加:",
        arr.cumsum(axis=0),
        "1轴的累积:",
        arr.cumprod(axis=1))
# teststatis()


# 用于布尔型数组的方法
def boolarray():
    # 布尔值会被强制转换为1（True）和0（False）。因此，sum经常被⽤来对布尔型数组中的True值计数
    arr = np.random.randn(100)
    # 因为arr>0产生的就是一个数组
    printn("bool数组:", arr > 0, "True值计数:", (arr > 0).sum())
    # any⽤于测试数组中是否存在⼀个或多个True，⽽all则检查数组中所有值是否都是True
    bools = np.array([False, False, True, False])
    printn("是否有true:", bools.any(), "是否全是True:", bools.all())
# boolarray()


# 排序
# 跟Python内置的列表类型⼀样，NumPy数组也可以通过sort⽅法就地排序
def testsort():
    arr = np.random.randn(6)
    printn("数组:", arr)
    arr.sort()
    printn('排序后更改原数组:', arr)
    # 多维数组可以在任何⼀个轴向上进⾏排序，只需将轴编号传给sort即可
    arr = np.random.randn(5, 3)
    printn("数组2:", arr)
    arr.sort(1)
    printn('排序后更改原数组2:', arr)
    # 顶级⽅法np.sort返回的是数组的已排序副本
    # 计算数组分位数最简单的办法是对其进⾏排序，然后选取特定位置的值
    large_arr = np.random.randn(1000)
    large_arr.sort()  # 排序
    printn("5%分位:", large_arr[int(0.05 * len(large_arr))])
# testsort()


# 集合运算，唯⼀化以及其它的集合逻辑
'''
unique,intersect1d,union1d,in1d,setdiff1d,setxor1d
'''


def testset():
    # np.unique函数执行唯一化
    names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
    printn("唯一姓名:", np.unique(names))
    ints = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4])
    printn("唯一数字:", np.unique(ints))
    # np.in1d⽤于测试⼀个数组中的值在另⼀个数组中的成员资格，返回⼀个布尔型数组
    values = np.array([6, 0, 0, 3, 2, 5, 6])
    printn("是否在后一个数组中:", np.in1d(values, [2, 3, 6]))
# testset()


# ----------------------------------------------------------------------
# 用于数组的文件输入输出
# 确保目录存在，文件可以不存在，可以不写后缀名，默认加上.npy后缀
def testfile():
    arr = np.arange(10)
    np.save('./some_array', arr)
    # 读取文件内容,要写明后缀
    filetemp = np.load('./some_array.npy')
    printn("读取的数据:", filetemp)
    # 通过np.savez可以将多个数组保存到一个未压缩文件中，将数组以关键字参数传入
    np.savez('array_archive.npz', a=arr, b=arr)
    # 加载.npz文件，得到一个类似字典的文件，该对象会对各个数组进行延迟加载：
    arch = np.load('array_archive.npz')
    printn('npz-load:', arch['b'])
    # 若要将数据压缩，则使用np.savez_compressed
    np.savez_compressed('arrays_compressed.npz', a=arr, b=arr)
    arch2 = np.load('array_archive.npz')
    printn('npz_compressed-load:', arch2['b'])
# testfile()


# ------------------------------------------------------------------------
# 线性代数
def testmatrix():
    x = np.array([[1., 2., 3.], [4., 5., 6.]])
    y = np.array([[6., 23.], [-1, 7], [8, 9]])
    prodxy = x.dot(y)  # 只是一个视图,等价np.dot(x,y)
    printn('矩阵x为:', x, '矩阵y为:', y, '点积为:', prodxy)
    # 一个二维数组跟一个大小合适的一维数组的矩阵点积运算，得到一个一维数组，即m*n与n*1相乘得到m*1
    prod2 = np.dot(x, np.ones(3))
    printn('点积2:', prod2)
    # @也可用作中缀运算符，进行矩阵乘法,相当于np.dot()
    prod3 = x @ np.ones(3)
    printn('用@计算点积2:', prod3)
    # np.linalg中有一组标准的矩阵分解运算及诸如求逆和行列式之类的东西。
    '''
    diag--以1维数组形式返回方阵的对角线元素，或将1维数组转换成方阵（非对焦为0）
    dot--矩阵乘法；trace--对角线元素之和；det--矩阵行列式；eig--方阵的特征值和特征向量
    inv--方阵的逆；pinv--矩阵的Moore—Penrose伪逆；qr--计算QR分解
    svd--奇异值分解；solve--解线性方程组Ax=b，A为方阵；lstsq--Ax=b的最小二乘解
    '''
    from numpy.linalg import inv, qr
    X = np.random.randn(5, 5)
    printn("随机数组:", X)
    mat = X.T.dot(X)  # X的转置乘X
    invx = inv(mat)  # 求逆
    printn("mat的逆:", invx)
    matd = mat.dot(invx)  # A*(A的逆)
    printn('mat点乘其逆:', matd)
    q, r = qr(mat)  # QR分解
    printn('r值:', r)
# testmatrix()


# 伪随机数生成
# numpy增加了⼀些⽤于⾼效⽣成多种概率分布的样本值的函数
'''
seed--种子；permutation--返回一个序列的随机排列或一个随机排列的范围
shuffle--对一个序列就地随机排列
rand--产生均匀分布的言本质
randint--从给定的上下限范围内随机选取整数,左闭右开
randn--产生正态分布(平均值为0，标准差为1)的样本值
binomial--产生二项分布的样本值
normal--产生正态(高斯)分布的样本值
beta--产生Beta分布的样本值
chisquare--产生卡方分布的样本值
gamma-产生Gamma分布的样本值
uniform--产生[0，1)中均匀分布的样本值
'''


def testrandom():
    # normal来得到⼀个标准正态分布的样本数组
    samples = np.random.normal(size=(4, 4))
    printn("标准正态分布:", samples)
    # 更改全局的随机数种子
    np.random.seed(1234)
    # 以使⽤numpy.random.RandomState，创建⼀个与其它隔离的随机数⽣成器
    rng = np.random.RandomState(1234)
    printn("随机数种子生成的:", rng.randn(10))
# testrandom()


# 随机漫步
def randomstep():
    # 从0开始，步⻓1和－1出现的概率相等
    # ⽤np.random模块⼀次性随机产⽣1000个“掷硬币”结果（即两个数中任选⼀个），将其分别设置为1或－1,计算累计和
    nsteps = 1000
    draws = np.random.randint(0, 2, size=nsteps)
    steps = np.where(draws > 0, 1, -1)
    walk = steps.cumsum()
    # 沿着漫步路径做⼀些统计⼯作了，⽐如求取最⼤值和最⼩值
    printn("最小值:,", walk.min(), "最大值:", walk.max())
    # 获取首次穿越时间，随机漫步过程中第⼀次到达某个特定值的时间。
    # 需要多久才能距离初始0点⾄少10步远（任⼀⽅向均可）
    '''
    np.abs(walk)>=10可以得到⼀个布尔型数组，
    它表示的是距离是否达到或超过10，⽽我们想要知道的是第⼀个10或－10的索引。
    可以⽤argmax来解决这个问题，它返回的是该布尔型数组第⼀个最⼤值的索引（True就是最⼤值）
    '''
    maxindex = (np.abs(walk) >= 10).argmax()  # argmax并不是最好的，因为他会对数组全部扫描
    print("首次时间为:", maxindex)
# randomstep()


def morerandomsteps():
    # ⼀次性计算5000个随机漫步过程（⼀⾏⼀个）
    nwalks = 5000
    nsteps = 1000
    # 给numpy.random的函数传⼊⼀个⼆元元组就可以产⽣⼀个⼆维数组
    draws = np.random.randint(0, 2, size=(nwalks, nsteps))
    steps = np.where(draws > 0, 1, -1)
    walks = steps.cumsum(1)  # 1轴计算，即行
    printn("每行的随机漫步累计和:", walks)
    # 计算所有随机漫步过程的最大值和最小值
    printn("所有漫步的最大值:", walks.max(), "所有漫步的最小值:", walks.min())
    # 计算30或-30的最小穿越时间，因为不是5000个过程都达到了30，所以要用any判断
    hits30 = (np.abs(walks) >= 30).any(1)
    printn("达到30或-30的漫步数:", hits30.sum())
    # 利⽤这个布尔型数组选出那些穿越了30（绝对值）的随机漫步（⾏）,并调⽤argmax在轴1上获取穿越时间
    crossing_times = (np.abs(walks[hits30]) >= 30).argmax(1)
    printn("所有穿越了30或-30的平均时间:", crossing_times.mean())
# morerandomsteps()
